package cn.zwx.zookeeper.config.example.source;

import cn.zwx.zookeeper.config.example.config.ymal.ApplicationRootConfiguration;
import cn.zwx.zookeeper.config.example.config.ymal.ZookeeperConfig;
import cn.zwx.zookeeper.config.example.connect.ZookeeperConnectHandler;
import cn.zwx.zookeeper.config.example.listener.NodeDataChangeCuratorCacheListener;
import cn.zwx.zookeeper.config.example.utils.ProcessNestMapUtil;
import cn.zwx.zookeeper.config.example.utils.YamlLoader;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.CuratorCache;
import org.apache.curator.framework.recipes.cache.CuratorCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 *  加载远程配置
 *  @author zhangwenxue
 */
public class ZookeeperPropertySourceLocator implements PropertySourceLocator{

    private static final Logger logger = LoggerFactory.getLogger(ZookeeperPropertySourceLocator.class);


    private final String DATA_NODE="/data";

    private final String RESOURCE_FILE = "application.yaml";

    private final CuratorFramework curatorFramework;


    /**
     * 启动zookeeper
     */
    public ZookeeperPropertySourceLocator() {
        curatorFramework = this.loadCuratorFramework();
        curatorFramework.start();
    }

    /**
     * 加载配置
     */
    private CuratorFramework loadCuratorFramework() {
        ZookeeperConfig zookeeperConfig = null;
        try {
            ClassLoader classLoader = getClass().getClassLoader();
            // 这里可以优化自己代独立的配置文件
            URL resource = classLoader.getResource(RESOURCE_FILE);
            if (Objects.nonNull(resource) && Objects.nonNull(resource.getFile())){
                File file = new File(resource.getFile());
                zookeeperConfig = YamlLoader.loadForZookeeperConfig(file, ApplicationRootConfiguration.class);
            }
        }catch (Exception e){
            throw new RuntimeException("load zookeeper config fail",e);
        }
        if (Objects.isNull(zookeeperConfig)){
            throw new RuntimeException("load zookeeper config is not empty");
        }
        ZookeeperConnectHandler zookeeperConnectHandler = new ZookeeperConnectHandler(zookeeperConfig);
        return zookeeperConnectHandler.getCuratorFramework();
    }


    @Override
    public PropertySource<?> locate(Environment environment, ConfigurableApplicationContext applicationContext) {
        //加载远程Zookeeper的配置保存到一个PropertySource
        logger.info("开始加载外部化配置");
        CompositePropertySource composite=new CompositePropertySource("configService");
        try {
            Map<String,Object> dataMap= this.getRemoteEnvironment();
            MapPropertySource mapPropertySource=new MapPropertySource("configService",dataMap);
            composite.addPropertySource(mapPropertySource);
            //添加节点的数据变更的事件监听
            this.addListener(environment,applicationContext);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return composite;
    }

    private void addListener(Environment environment, ConfigurableApplicationContext applicationContext) {
        NodeDataChangeCuratorCacheListener ndc =new NodeDataChangeCuratorCacheListener(environment,applicationContext);
        CuratorCache curatorCache=CuratorCache.build(curatorFramework,DATA_NODE,CuratorCache.Options.SINGLE_NODE_CACHE);
        CuratorCacheListener listener=CuratorCacheListener
                .builder()
                .forChanges(ndc).build();
        curatorCache.listenable().addListener(listener);
        curatorCache.start();
    }


    private Map<String, Object> getRemoteEnvironment() throws Exception {
        // zookeeper 默认gbk 需要出来下 才不会乱码
        String data= new String (curatorFramework.getData().forPath(DATA_NODE),"gbk");
        //支持JSON格式
        Map<String, Object> objectObjectHashMap = new HashMap<>(8);
        ObjectMapper objectMapper=new ObjectMapper();
        Map<String,Object> map= objectMapper.readValue(data,Map.class);
        ProcessNestMapUtil.processNestMap("",objectObjectHashMap,map);
        return objectObjectHashMap;
    }



}
